#include<iostream>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<vector>
#include<sys/wait.h>
#include"Task.hpp"

//master
class Channel//与子进程通信的信道
{
    public:
        Channel(int wfd,pid_t id,const std::string& name)
        :_wfd(wfd),_subprocessid(id),_name(name)
        {}
        ~Channel()
        {}
        int GetWfd()
        {
            return _wfd;
        }
        std::string GetName()
        {
            return _name;
        }
        pid_t GetProcessId()
        {
            return _subprocessid;
        }
        void CloseChannel()
        {
            close(_wfd);
        }
        void Wait()
        {
            pid_t rid=waitpid(_subprocessid,nullptr,0);
            if(rid>0)
            {
                std::cout<<"wait "<<rid<<" success"<<std::endl;
            }
        }
    private:
        int _wfd;
        pid_t _subprocessid;
        std::string _name;
};


void work(int rfd)
{
    while(true)
    {
       int command=0;
       int n=read(rfd,&command,sizeof(command));
       if(n==sizeof(int))
       {
            std::cout<<"my pid is "<<getpid()<<", handler task "<<std::endl;
            Excutetask(command);
       }
       else if(n==0)
       {
            std::cout<<"sub process:"<<getpid()<<" quit"<<std::endl;
            break;
       }
    }
}

void CreatChannelAndSub(int num,std::vector<Channel> *channels)
{
     for(int i=0;i<num;i++)
    {
        int pipefd[2]={0};
        int n=pipe(pipefd);
        if(n<0)exit(1);
        pid_t id=fork();
        if(id==0)
        {
            if(!channels->empty())
            {
                for(auto &channel:*channels)
                {
                    channel.CloseChannel();//解决创建子进程时继承父进程的文件描述表中对其他的信道的写端打开的问题
                }
            }
            close(pipefd[1]);
            //dup2(pipefd[0],0)将管道读端重定向到标准输入，word就不需要参数，子进程的文件描述符0指向了管道读端
            work(pipefd[0]);
            close(pipefd[0]);
            exit(0);
        }
        std::string name="channel-"+std::to_string(i);
        close(pipefd[0]);
        channels->push_back(Channel(pipefd[1],id,name));
    }
}

int NextChannel(int channelnum)
{
    static int next=0;
    int channel=next;
    next++;
    next%=channelnum;
    return next;
}

void SendTaskCommand(Channel &channel,int taskcommand)
{
    write(channel.GetWfd(),&taskcommand,sizeof(taskcommand));
}
void CtrlProcess(std::vector<Channel> &channels)
{
    int cnt=10;
    while(cnt--)
    {
        sleep(1);
        int taskcommand=SelectTask();

        int index_channel=NextChannel(channels.size());
        SendTaskCommand(channels[index_channel],taskcommand);
        std::cout<< "taskcommand:" <<taskcommand<<", channel:"
        <<channels[index_channel].GetName()<<", subprocessid:"
        <<channels[index_channel].GetProcessId()<<std::endl;
    }
}

void CleanUpChannel(std::vector<Channel> &channels)
{
    for(auto channel:channels)
    {
        channel.CloseChannel();//递归关闭，由于创建信道的文件描述符继承bug只有最后一个管道关了才能关前面的
    }
    for(auto channel:channels)
    {
        channel.Wait();
    }
}
int main(int argc,char *argv[])
{
    if(argc!=2)
    {
        std::cerr<<"Usage"<<argv[0]<<"processnum"<<std::endl;
    }
    int num=std::stoi(argv[1]);
    LoadTask();
    std::vector<Channel> channels;//管理信道
    CreatChannelAndSub(num,&channels);//创建子进程和对应信道
    CtrlProcess(channels);//轮询分派任务给子进程
    CleanUpChannel(channels);//关闭写端回收子进程
    


    return 0;
}